#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "listaligada.c"
#include "mpi.h"

#define BLOCOS_POR_NO 1000
#define TAM_PALAVRA 50

int main(int argc, char **argv){

	int myRank, worldSize;
	char *message;

	MPI_Status status;
    Apontador pos=NULL;
    char c, palavra[TAM_PALAVRA];
	FILE *fileInicial, *file;


	MPI_Init(&argc, &argv);
	MPI_Comm_size(MPI_COMM_WORLD, &worldSize);
	MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
		
	if (myRank==0){
		int i=0, j=0, k=0, totalPalavras=0, rank, flagRank=0, caracter, resultPalavras=0;
		int totalBlocos, controleBlocos=1, result;
		long fileSize, bytesPorBloco, bytesSobrando;
		float completado=0.1;
		time_t start,end;
		
		time (&start);
		
		fileInicial=fopen("palavras.txt", "r+");
		if (file==NULL){
			printf("Erro ao abrir o arquivo.\n");
			exit(1);
		}
		file=fopen("arrumado.txt", "w+");
		if (file==NULL){
			printf("Erro ao abrir o arquivo.\n");
			exit(1);
		}

		//Calcula quantidade de palavras no arquivo e arruma saida
		while(fscanf(fileInicial,"%c", &c)>0){
			if(c !=' ' && c !=',' && c !='.' && c !='!' && c !='?' && c!='\n' && c!='-'){
				caracter=c;
				if((caracter>=65 && caracter<=90)||(caracter>=97 && caracter<=122))
					palavra[i++]=c;
			}
			else if(i!=0){			
				palavra[i]='\0';
				//Zera o contador para começar a contar a proxima palavra
				i=0;
				fprintf(file, "%s\n", palavra);
				totalPalavras++;
			}
		}
		fclose(fileInicial);

		//Calcula quantidade de bytes no arquivo
		fseek (file, 0, SEEK_END);
		fileSize= ftell(file);
		rewind (file);

		totalBlocos= (worldSize-1)*(BLOCOS_POR_NO);
		bytesPorBloco=fileSize/totalBlocos;
		bytesSobrando= fileSize-(bytesPorBloco*totalBlocos);

		time (&end);	
		printf("Termino do processamento de palavras.\n");
		printf("---Tempo: %.2lf\n", difftime(end,start));
		printf("---Quantidade total de palavras: %d\n", totalPalavras);
		printf("---Tamanho do arquivo: %ld\n", fileSize);
		printf("---Quantidade total de blocos: %d\n", totalBlocos);
		printf("---Quantidade de bytes por bloco: %ld\n", bytesPorBloco);
		printf("---Bytes extras no ultimo bloco: %ld \n\n", bytesSobrando);
		
		

		time (&start);
		message=(char*)malloc(bytesPorBloco+100);
		if(message==NULL){
			printf("Falha ao alocar espaço.\n");
			exit(1);
		}
		
		i=0;
		k=0;
		rank=1;
		while(k<totalPalavras){
			fgets(palavra,TAM_PALAVRA-1,file);
			if((i+strlen(palavra))<bytesPorBloco){
				for(j=0;j<strlen(palavra);j++){
					message[i++]=palavra[j];
				}
				message[i]='\0';
				k++;
			}
			else{
				if(flagRank!=0){
					//recebe resultado
					MPI_Recv(&result,		//buffer da mensagem
							 1,				//quantidade MAX de itens
							 MPI_INT,		//de tipo char
							 MPI_ANY_SOURCE,//recebe do master
							 0,				//TAG
							 MPI_COMM_WORLD,//comunicador default
							 &status);		//infos
					resultPalavras+=result;
					time (&end);
					printf("Bloco terminado.\n");
					printf("---Tempo: %.2lf\n", difftime(end,start));
					printf("---Palavras processadas nesse bloco: %d\n", result);
					printf("---Total de palavras processadas: %d\n", resultPalavras);
					printf("---Porcentagem processada: %f\n\n", ((float)resultPalavras/(float)totalPalavras));

					/*if(completado<=((float)resultPalavras/(float)totalPalavras)){
						time (&end);
						printf("Terminado: %f%% em %.2lf segundos\n", completado, difftime (end,start));
						completado+=0.1;
					}*/
									
					//manda novo bloco
					MPI_Send(message,		//buffer da mensagem
						 i+1,				//quantidade de itens
						 MPI_CHAR,			//de tipo char
						 status.MPI_SOURCE, //manda para quem acabou de enviar Recv
						 0,					//TAG
						 MPI_COMM_WORLD);   //comunicador default
					//printf("2.Sending to node=%d, tamanho=%d\n", status.MPI_SOURCE, i+1);
				}
				else{
					MPI_Send(message,			//buffer da mensagem
							 i+1,				//quantidade de itens
							 MPI_CHAR,			//de tipo char
							 rank,				//rank de quem vai receber
							 0,					//TAG
							 MPI_COMM_WORLD);	//comunicador default
						//printf("1.Sending to node=%d, tamanho=%d\n", rank, i+1);
					rank++;
					if(rank==worldSize)
						flagRank=1;
				}
				i=0;
				for(j=0;j<strlen(palavra);j++)
					message[i++]=palavra[j];
				message[i]='\0';
				controleBlocos++;
				k++;
			}//else ((i+strlen(palavra))<bytesPorBloco)
		}//while
		message[0]='0';
		message[1]='\0';
		for(rank=1;rank<worldSize;rank++){
			MPI_Recv(&result,		//buffer da mensagem
							 1,				//quantidade MAX de itens
							 MPI_INT,		//de tipo char
							 MPI_ANY_SOURCE,//recebe do master
							 0,				//TAG
							 MPI_COMM_WORLD,//comunicador default
							 &status);		//infos
					resultPalavras+=result;
					time (&end);
					printf("Bloco terminado.\n");
					printf("---Tempo: %.2lf\n", difftime(end,start));
					printf("---Palavras processadas nesse bloco: %d\n", result);
					printf("---Total de palavras processadas: %d\n", resultPalavras);
					printf("---Porcentagem processada: %f\n\n", ((float)resultPalavras/(float)totalPalavras));

			MPI_Send(message,			//buffer da mensagem
					 2,					//quantidade de itens
					 MPI_CHAR,			//de tipo char
					 rank,				//rank de quem vai receber
					 0,					//TAG
					 MPI_COMM_WORLD);	//comunicador default
			printf("3.Sending to node=%d, message=%s\n", rank, message);
		}
	fclose(file);
	free(message);
	}//myRank==0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	else{
		int count=0, qtPalavras=0, qtTotalPalavras, aux=0, i=0, j=0, k=0, caracter, tamanhoPalavra=3, auxtam;
		TipoLista L1, L2, L3, L4, L5;	
		TipoItem I;
		srand(time(NULL));
		
		InicializaLista(&L1);  
		InicializaLista(&L2);
		InicializaLista(&L3);  
		InicializaLista(&L4);  
		InicializaLista(&L5);
		 	
		message=(char*)malloc(50000);
			if(message==NULL){
				printf("Falha ao alocar espaço.\n");
				exit(1);
			}
		while(1){
			MPI_Recv(message,	//buffer da mensagem
					 50000,			//quantidade MAX de itens
					 MPI_CHAR,		//de tipo char
					 0,				//recebe do master
					 0,				//TAG
					 MPI_COMM_WORLD,//comunicador default
					 &status);		//infos
			if(message[0]=='0'){
			printf("Break! Node: %d\n", myRank);
				break;
			}
			MPI_Get_count(&status, MPI_CHAR, &count);
			//printf("\nCount: %d My rank= %d Message:.%s.\n", count, myRank, message);
			//printf("\nCount: %d, recebido.My rank= %d\n", count, myRank);

			i=0;
			aux=0;
			qtTotalPalavras=0;
			while(aux<count){
				if(message[aux] !=' ' && message[aux] !=',' && message[aux] !='.' && message[aux] !='!' && message[aux] !='?' && message[aux]!='\n' && message[aux]!='-' && message[aux]!='\0'){
					I.Chave[i++] = tolower(message[aux]);
				}
				else if(i!=0){
					I.Chave[i] = '\0';
					qtTotalPalavras++;
					//printf("Rank: %d, Palavra: %s\n", myRank, I.Chave);
					if(i<=5){
						switch(i){
							case 1:
								InsereLista(&L1, I, &qtPalavras);
							break;
							case 2:
								InsereLista(&L2, I, &qtPalavras);
								break;
							case 3:
								InsereLista(&L3, I, &qtPalavras);
								break;
							case 4:
								InsereLista(&L4, I, &qtPalavras);
								break;
							case 5:
								InsereLista(&L5, I, &qtPalavras);
								break;
							default:
								printf("Tamanho da palavra inválido. O programa será abortado.\n");
						}
					}
					else{
						for(j=0;j<i;j++)
							palavra[j] = I.Chave[j];
						palavra[j]='\0';
						j=0;
						//Separa a palavra em grupos de 3, e o restante, caso necessario
						while(j<i){
							for(k=0;k<tamanhoPalavra;k++)
								I.Chave[k]=palavra[j++];
							I.Chave[tamanhoPalavra]='\0';
							switch(tamanhoPalavra){
								case 1:
									InsereLista(&L1, I, &qtPalavras);
								break;
								case 2:
									InsereLista(&L2, I, &qtPalavras);
									break;
								case 3:
									InsereLista(&L3, I, &qtPalavras);
									break;
								case 4:
									InsereLista(&L4, I, &qtPalavras);
									break;
								case 5:
									InsereLista(&L5, I, &qtPalavras);
									break;
								default:
									printf("Tamanho da palavra inválido. O programa será abortado.\n");
							}
							auxtam=i-j;
							if(auxtam==4)
								tamanhoPalavra=4;
							if(auxtam<3)
								tamanhoPalavra=auxtam;
						}
					}
					//Zera o contador para começar a ler a proxima palavra
					i=0;
					//Redefine o tamanho default de 3
					tamanhoPalavra=3;
				}
				aux++;
			}//while que insere palavras
			
			//Gera palavras e tenta consumir, enquanto tiverem palavras não geradas
			while(qtPalavras>0){
			
				//Determina o tamanho da palavra a ser gerada. Varia de 1 a 5
				tamanhoPalavra = rand()%5+1;
			
				//Gera a palavra do tamanho determinado
				for(i=0;i<tamanhoPalavra;i++){	
					caracter = rand()%26+97;
					palavra[i]=caracter;
				}
				palavra[i]='\0';
				//Procura a palavra na lista apropriada
				switch(tamanhoPalavra){
					case 1:
						pos = PesquisaLista(&L1, palavra);
						if(pos!=NULL){
							RemoveLista(&L1, pos);
							qtPalavras--;
						}
						break;
					case 2:
						pos = PesquisaLista(&L2, palavra);
						if(pos!=NULL){
							RemoveLista(&L2, pos);
							qtPalavras--;
						}
						break;
					case 3:
						pos = PesquisaLista(&L3, palavra);
						if(pos!=NULL){
							RemoveLista(&L3, pos);
							qtPalavras--;
						}
						break;
					case 4:
						pos = PesquisaLista(&L4, palavra);
						if(pos!=NULL){
							RemoveLista(&L4, pos);
							qtPalavras--;
						}
						break;
					case 5:
						pos = PesquisaLista(&L5, palavra);
						if(pos!=NULL){
							RemoveLista(&L5, pos);
							qtPalavras--;
						}
						break;
					default:
						printf("Tamanho da palavra inválido. O programa será abortado.\n");
						exit(1);
				}
			}//while que gera palavras

			MPI_Send(&qtTotalPalavras,	//buffer da mensagem
					 1, 				//quantidade de itens
					 MPI_INT,			//de tipo int
					 0,					//envia para o master
					 0,					//TAG
					 MPI_COMM_WORLD);	//comunicador default
				//printf("Rank: %d sending to master, message=%d, qtPalavras: %d\n", myRank, qtTotalPalavras, qtPalavras);
		}
	free(message);
	}

	MPI_Finalize();
return 0;
}
